Namespace ShapeLibrary.Design
    '''
    '''     Each shape has its own designer.  This is a base class that
    '''     all individaul shape designers derive from.  It's mostly 
    '''     abstract and is used to provide a way to manipulate different
    '''     shapes at design time.
    '''
    Friend MustInherit Class ShapeDesigner
        Inherits ComponentDesigner
        Private Shared ReadOnly m_adornmentDimensions As Size = New Size(7, 7)

        ' This is a hit test object returned from GetHitTest.  ShapeDesigner
        ' only defines a hit test object that all shapes support: movement.
        '
        Protected Shared ReadOnly HitMove As Object = New Object()

        '''
        '''     This friend static property returns the size of the adornments
        '''     we are drawing.  
        '''
        Friend Shared ReadOnly Property AdornmentDimensions() As Size
            Get
                Return m_adornmentDimensions
            End Get
        End Property

        '''
        '''     This method returns the bounding box for the shape.  When
        '''     set, this forces the shape to conform to the bounding box.
        '''     We offer this as a design time property so that anyone can
        '''     get at it.  This allows us to treat all shapes the same at
        '''     design time -- we just manipulate a bounding rectangle --
        '''     but still allow the runtime object model of the shape to be
        '''     clean.
        '''
        '''     Note that we add a bunch of attributes here.  The attributes
        '''     perform the following functions:
        '''
        '''         Browsable(false)
        '''             This prevents the property grid from showing this
        '''             property.  We want users to manipulate the actual
        '''             shape properties and not this special bounding box
        '''             property.
        '''
        '''         DesignOnly(true)
        '''             Specifies that this property is not part of the 
        '''             runtime object model.  This ensures that the code
        '''             generator does not actually generate a property set
        '''             to the Bounds property in code.  If it did, the 
        '''             code wouldn't compile because this property doesn't
        '''             actually exist on the runtime object.
        '''
        '''         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
        '''             This attribute tells the code engine that there is no 
        '''             need to ever generate code for this property.  We have marked
        '''             this property as design only, but that just routes the
        '''             property into a resource file, rather than generating
        '''             code.  This attribute prevents the property from being
        '''             written out at all.  Technically this is redundant
        '''             with DesignOnly because this attribute prevents any
        '''             writing of the property.
        '''
        <Browsable(False), _
        DesignOnly(True), _
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
        Protected MustOverride Property BoundingBox() As Rectangle

        '''
        '''     This method is used to perform hit-testing at design time.  The
        '''     root designer for shapes will call this for every mouse move to
        '''     determine if the mouse is hovering over a shape.  The object
        '''     that is returned should be null if the coordinates are not
        '''     on the shape.  If the coordinates are on the shape, the object
        '''     that is returned should be some identifier that this designer
        '''     can later use.  The object will be passed to the GetCursor,
        '''     DrawDragFeedback and Drag methods.
        '''
        '''     Parameters:
        '''     -----------
        '''     x: The X coordinate of the hit test.  These coordinates are in the
        '''     same coordinate system as the shape, so direct comparisons can
        '''     be made.
        '''
        '''     y: The Y coordinate of the hit test.  These coordinates are in the
        '''     same coordinate system as the shape, so direct comparisons can
        '''     be made.
        '''
        '''     Returns:
        '''     --------
        '''     An object that identifes the hit, or null if
        '''     the point is not within our shape.
        '''
        Friend MustOverride Function GetHitTest(ByVal x As Integer, ByVal y As Integer) As Object

        '''
        '''     Returns the cursor for the given hit object.
        '''
        '''     Parameters:
        '''     -----------
        '''     hitObject: An object that has been previously returned from
        '''     the GetHitTest method.  This will never be passed
        '''     null.
        '''
        '''     Returns:
        '''     --------
        '''     The cursor that should be displayed for the given 
        '''     hit location.
        '''
        Friend MustOverride Function GetCursor(ByVal hitObject As Object) As Cursor

        '''
        '''     Called at the end of a successful drag to actually
        '''     move the shape.  The X and Y values are offsets
        '''     to the drag, so they can simply be added to the
        '''     component's coordinates.  The object returned
        '''     from GetHitTest is also passed, allowing drag
        '''     to know if it should move, resize or do some
        '''     other manipulation to the object.
        '''
        '''     Parameters:
        '''     -----------
        '''     hitObject: The object returned from a prior call to GetHitTest.
        '''
        '''     x: The amount to offset the shape in the X direction.
        '''
        '''     y: The amount to offset the shape in the Y direction.
        '''
        Friend MustOverride Sub Drag(ByVal hitObject As Object, ByVal x As Integer, ByVal y As Integer)

        '''
        '''     Used to draw the correct adnornments for this object.
        '''     This will be called only if the object is currently
        '''     selected.  The designer should draw the appropriate 
        '''     grab handles on the object.
        '''
        '''     Parameters:
        '''     -----------
        '''     g: The graphics object to draw the adornments to.  The
        '''     coordinate system will be preset to match the coordinates
        '''     of the shape.
        '''
        '''     primary: This will be true if this shape should be considered
        '''     to be the "primary" or "key" shape in the selection.
        '''
        Friend MustOverride Sub DrawAdornments(ByVal g As Graphics, ByVal primary As Boolean)

        '''
        '''     Used to draw drag feedback to the user.  Drag feedback
        '''     should be drawn so that the first call to draw feedback at
        '''     a given position will draw it, and the second call will remove
        '''     it.  The class ControlPaint can be of use here as it provides
        '''     several methods that begin with "DrawReversible".  These methods
        '''     draw lines and shapes in such a way as to allow them to be
        '''     erased when called a second time.  These methods also always use
        '''     global coordinates.  Because using global coordinates is common for
        '''     these methods, DrawDragFeedback is also provided a screenOffset
        '''     parameter that can be used to convert the shape's coordinates to 
        '''     global coordiniates.
        '''
        '''     Parameters:
        '''     -----------
        '''     hitObject: The object returned from a prior call to GetHitTest.
        '''
        '''     backColor: The color of the background that shows behind the shapes.  Often this is necessary 
        '''     when drawing reversible feedback.
        '''
        '''     screenOffset: This offset can be added to shape coordinates to convert them to global coordinates.
        '''     By adding these values to shape coordinates, you can directly call any of the
        '''     DrawReversible methods in ControlPaint.
        '''
        '''     mouseOffset: The current offset of the mouse from where the drag began.  This starts at 0,0 when
        '''     the user initiates a drag and then changes depending on how far the mouse has
        '''     moved from its starting position.
        '''
        Friend MustOverride Sub DrawDragFeedback(ByVal hitObject As Object, ByVal backColor As Color, ByVal screenOffset As Point, ByVal mouseOffset As Point)

        '''
        '''     This overrides PreFilterProperties on ComponentDesigner.  The set of PreFilter and 
        '''     PostFilter methods are used to modify the metadata that a component presents.  The
        '''     PreFilter methods are used when you want to add or remove specific pieces of metadata.
        '''     The PostFilter methods are used when you want to make changes to all pieces of
        '''     metadata. For example, we are adding a new property here, so we use PreFilterProperites.
        '''     If we wanted to something global to properties, like change all properties on a
        '''     component to read-only, we would do this in PostFilterProperties.  The only
        '''     rule to follow here is that in PreFilter, always call base.PreFilter before your
        '''     own code, and in PostFilter, always call base.PostFilter last.
        '''
        '''     Parameters:
        '''     -----------
        '''     properties: A dictionary of properties to filter.  The keys of the dictionary are property
        '''     names and the values are PropertyDescriptors.  This dictionary can be 
        '''     modified in this method.
        '''
        Protected Overrides Sub PreFilterProperties(ByVal properties As IDictionary)
            ' Always call base first!
            '
            MyBase.PreFilterProperties(properties)

            ' We add a new property:  BoundingBox.
            '
            properties("BoundingBox") = TypeDescriptor.CreateProperty(Me.GetType(), "BoundingBox", GetType(Rectangle))
        End Sub
    End Class
End Namespace
